<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
import {
  ApplyP2PCall,
  DescriptionP2PCall,
  IceP2PCall,
  NotifyFrameBuilt,
  ResponseP2PCall
} from '../utils/P2PCall'
import { delay, formatDate } from '../utils/tool'
import { LoginUser, RemoteUser } from '../utils/IpcData'
import callAudio from '../assets/p2pCall.mp3'
import handoutAudio from '../assets/handoutClose.mp3'
import { saveCallRecord } from '../api/p2pCall'

const store = useStore()
const userId = computed(() => store.getters.getUserId)
const nickname = computed(() => store.getters.getUserNickname)
const avatar = computed(() => store.getters.getUserAvatar)
const targetId = computed(() => store.getters.getCallRemoteId)
const targetName = computed(() => store.getters.getCallRemoteNickname)
const targetAvatar = computed(() => store.getters.getCallRemoteAvatar)

const callAudioPlayer = ref<HTMLAudioElement | null>(null)
const handoutAudioPlayer = ref<HTMLAudioElement | null>(null)
const callType = ref('')
const localVideo = ref<HTMLVideoElement | null>(null)
const remoteVideo = ref<HTMLVideoElement | null>(null)
const localStream = ref<MediaStream | null>(null)
const remoteVideoLoader = ref(true)
const mutedStatus = ref(false)
const micStatus = ref(true)
const hasAnswer = ref(false)
const hasOffer = ref(false)
const hasIce = ref(false)

let localPeerCon: RTCPeerConnection | null = null

let startTime = null

// 初始化vuex
onMounted(() => {
  setTimeout(() => {
    window.electron.ipcRenderer.send('initVuex')
  }, 200)
})

// 存储Vuex信息并初始化LocalVideo
window.electron.ipcRenderer.on('initVuex', async (_, args) => {
  console.log('存储Vuex信息并初始化LocalVideo视频流')

  const loginUser: LoginUser = args[0]
  const remoteUser: RemoteUser = args[1]
  store.commit('SET_USER_ID', loginUser.userId)
  store.commit('SET_NICKNAME', loginUser.nickname)
  store.commit('SET_AVATAR', loginUser.avatar)
  store.commit('SET_CALL_REMOTE_ID', remoteUser.userId)
  store.commit('SET_CALL_REMOTE_NICKNAME', remoteUser.nickname)
  store.commit('SET_CALL_REMOTE_AVATAR', remoteUser.avatar)
  store.commit('SET_CALL_IS_CALLER', remoteUser.isCaller)
  callType.value = remoteUser.callType
  console.log('callType:', callType.value)

  await createLocalVideoStream()

  // 如果当前用户是发起方，则发起请求通话至服务器中
  if (store.getters.getCallIsCaller) {
    const applyVideoCall = new ApplyP2PCall(
      userId.value,
      nickname.value,
      avatar.value,
      targetId.value,
      targetName.value,
      targetAvatar.value,
      callType.value
    )

    window.electron.ipcRenderer.send('apply-p2p-call', applyVideoCall)
    // 开始计时，如果两分钟未答应。则消除窗口
    delay(120000).then(() => {
      if (remoteVideoLoader.value) {
        const res = new ResponseP2PCall(targetId.value, userId.value, false, '请求通话超时')
        window.electron.ipcRenderer.send('refuse-p2p-call', res)
        // 通知服务器，发起方已取消请求
        handleHandout()
      }
    })

    if (callAudioPlayer.value && handoutAudioPlayer.value) {
      await callAudioPlayer.value.play()
    }
  } else {
    // 通知发起方可以传输数据
    window.electron.ipcRenderer.send('notify-p2p-frame-built', store.getters.getCallRemoteId)
  }
})

window.electron.ipcRenderer.on('call-local-p2p-disconnect', () => {
  const res = new ResponseP2PCall(targetId.value, userId.value, false, '对方已挂断')
  window.electron.ipcRenderer.send('refuse-p2p-call', res)
  handleHandout()
})

// 处理接收方的通话请求响应
window.electron.ipcRenderer.on('handle-p2p-call-response', (_, args) => {
  const resp: ResponseP2PCall = args
  // 如果接受通话请求，则初始化LocalPeerConn
  if (resp.respStatus) {
    console.log('当前端创建中...')
    localPeerCon = new RTCPeerConnection()

    if (localStream.value) {
      console.log('配置当前连接音轨和视频轨...')
      for (const track of localStream.value.getTracks()) {
        localPeerCon.addTrack(track, localStream.value)
        // 记录开始时间
        startTime = formatDate(new Date())
      }
    }
  } else {
    // 不同意，销毁当前窗口，并且发送不同意的原因。
    window.electron.ipcRenderer.send('refuse-p2p-call', resp)
    handleHandout()
  }
})

// 当发起方接收到对方已经可以开始接收数据时，开始发送offer和ICE至对方
window.electron.ipcRenderer.on('receive-p2p-frame-built', async () => {
  if (localPeerCon) {
    // 发送ICE至接收方
    localPeerCon.onicecandidate = (event) => {
      if (event.candidate) {
        // 发送ICE到信令服务器
        console.log('发送ICE至信令服务器')
        window.electron.ipcRenderer.send(
          'send-p2p-ice',
          JSON.stringify(new IceP2PCall(targetId.value, event.candidate))
        )
      }
    }

    localPeerCon.ontrack = (event) => {
      console.log('监听到对方的track:', event)
      if (event && event.streams && remoteVideo.value) {
        remoteVideo.value.srcObject = event.streams[0]
        remoteVideoLoader.value = false
        if (callAudioPlayer.value && handoutAudioPlayer.value) {
          callAudioPlayer.value.pause()
        }
      }
    }

    console.log('发送offer...')
    const offer = await localPeerCon.createOffer()
    await localPeerCon.setLocalDescription(offer)
    window.electron.ipcRenderer.send(
      'send-p2p-offer',
      JSON.stringify(new DescriptionP2PCall(targetId.value, offer))
    )
  }
})

// 接收到offer后，发送answer和ice至对方
window.electron.ipcRenderer.on('receive-p2p-call-offer', (_, args) => {
  console.log('接收到发起方提供的offer:', args)
  const data: DescriptionP2PCall = args
  if (!localPeerCon && !hasOffer.value) {
    localPeerCon = new RTCPeerConnection()

    console.log('localStream.value', localStream.value)
    if (localStream.value) {
      console.log('配置当前连接音轨和视频轨...')
      for (const track of localStream.value.getTracks()) {
        localPeerCon.addTrack(track, localStream.value)
      }
    }

    // 发送ICE至发起方
    localPeerCon.onicecandidate = (event) => {
      if (event.candidate) {
        // 发送ICE到信令服务器
        console.log('发送ICE至信令服务器')
        window.electron.ipcRenderer.send(
          'send-p2p-ice',
          JSON.stringify(new IceP2PCall(targetId.value, event.candidate))
        )
      }
    }

    // 监听对方音频流
    localPeerCon.ontrack = (event) => {
      console.log('监听到对方的track:', event)
      if (event && event.streams && remoteVideo.value) {
        remoteVideo.value.srcObject = event.streams[0]
        remoteVideoLoader.value = false
        if (callAudioPlayer.value && handoutAudioPlayer.value) {
          callAudioPlayer.value.pause()
        }
      }
    }

    localPeerCon
      .setRemoteDescription(new RTCSessionDescription(data.desc))
      .then(async () => {
        console.log('成功设置远端Offer')
        hasOffer.value = true
        // 发送answer
        if (localPeerCon) {
          console.log('正在发送answer至对方')
          const answer = await localPeerCon.createAnswer()
          await localPeerCon.setLocalDescription(answer)
          window.electron.ipcRenderer.send(
            'send-p2p-answer',
            JSON.stringify(new DescriptionP2PCall(targetId.value, answer))
          )
        }
      })
      .catch((error) => {
        console.log('设置远端Offer失败:', error)
      })
  }
})

// 发起方接收answer
window.electron.ipcRenderer.on('receive-p2p-call-answer', (_, args) => {
  console.log('接收到远端的answer:', args)
  const data: DescriptionP2PCall = args
  if (localPeerCon && !hasAnswer.value) {
    localPeerCon
      .setRemoteDescription(new RTCSessionDescription(data.desc))
      .then(() => {
        console.log('成功设置远端Answer')
        hasAnswer.value = true
      })
      .catch((error) => {
        console.log('设置远端Answer失败:', error)
      })
  }
})

// 接收对方的ICE
window.electron.ipcRenderer.on('receive-p2p-call-ice', (_, args) => {
  if (localPeerCon && !hasIce.value) {
    const iceData: IceP2PCall = args

    if (iceData.candidate) {
      localPeerCon
        .addIceCandidate(new RTCIceCandidate(iceData.candidate))
        .then(() => {
          console.log('成功设置远端ICE')
          hasIce.value = true
        })
        .catch((error) => {
          console.log('设置远端ICE失败:', error)
        })
    }
  }
})

// 创建本地视频流
const createLocalVideoStream = async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: callType.value === 'video',
      audio: {
        echoCancellation: true,
        autoGainControl: true,
        noiseSuppression: true
      }
    })

    if (localVideo.value) {
      localStream.value = stream
      localVideo.value.srcObject = stream
    }
  } catch (error) {
    ElMessage.error('通话失败：无法获取摄像头和麦克风')
    console.log(error)
  }
}

// 开启和关闭声音
const changeMuteStatus = () => {
  mutedStatus.value = !mutedStatus.value
}

// 挂断
const handleHandout = () => {
  if (localPeerCon) {
    localPeerCon.close()
    localPeerCon = null
  }

  if (localStream.value) {
    localStream.value.getTracks().forEach((track) => {
      track.stop()
    })
  }

  // 因为发起方才给startTime赋值了，因此直接写caller就行
  if (startTime) {
    saveCallRecord(userId.value, targetId.value, callType.value, startTime, formatDate(new Date()))
  }

  handleExit()
}

// 开启和关闭麦克风
const changeMicStatus = () => {
  micStatus.value = !micStatus.value
  if (localStream.value) {
    const audioTrack = localStream.value.getAudioTracks()[0]
    audioTrack.enabled = micStatus.value
  }
}

// 关闭窗口并通知服务器，对方已断开
const handleExit = () => {
  if (callAudioPlayer.value && handoutAudioPlayer.value) {
    callAudioPlayer.value.pause()
    handoutAudioPlayer.value.play()
  }

  setTimeout(() => {
    window.electron.ipcRenderer.send(
      'close-communication-window',
      new NotifyFrameBuilt(store.getters.getCallRemoteId)
    )
    store.commit('FLUSH_ALL_P2P_CALL_DATA')
  }, 1000)
}
</script>

<template>
  <div class="h-7">
    <div style="-webkit-app-region: drag" class="float-left h-7 w-11/12"></div>
    <svg
      class="h-7 w-7 text-gray-500 hover:bg-red-500 hover:cursor-pointer hover:text-white float-right"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 20 20"
      fill="currentColor"
      @click="handleExit"
    >
      <path
        fill-rule="evenodd"
        d="M14.348 14.849a1 1 0 01-1.414 0L10 11.414l-2.93 2.93a1 1 0 11-1.415-1.415l2.928-2.929-2.929-2.93a1 1 0 111.415-1.414l2.93 2.929 2.93-2.929a1 1 0 011.414 1.414l-2.93 2.93 2.93 2.93a1 1 0 010 1.415z"
        clip-rule="evenodd"
      />
    </svg>
  </div>

  <audio ref="callAudioPlayer" class="w-0 h-0" :src="callAudio"></audio>
  <audio ref="handoutAudioPlayer" class="w-0 h-0" :src="handoutAudio"></audio>

  <div class="w-full pl-2 pr-2 flex flex-row">
    <div
      v-if="callType === 'voice'"
      class="w-1/2 flex flex-col justify-center items-center bg-gray-500"
      style="height: 300px"
    >
      <img
        alt="Avatar"
        class="w-20 h-20 rounded-full mr-3 object-scale-down border"
        :src="avatar"
      />
      <div class="font-bold text-white">{{ nickname }}</div>
      <video ref="localVideo" muted class="w-0 h-0" autoplay></video>
    </div>
    <video v-else ref="localVideo" muted class="w-1/2" playsinline autoplay></video>

    <div
      v-if="callType === 'voice'"
      class="w-1/2 flex flex-col justify-center items-center bg-gray-400"
      style="height: 300px"
    >
      <img
        alt="Avatar"
        class="w-20 h-20 rounded-full mr-3 object-scale-down border"
        :src="targetAvatar"
      />
      <div class="font-bold text-white">{{ targetName }}</div>
      <video ref="remoteVideo" class="w-0 h-0" autoplay></video>
    </div>
    <video v-else ref="remoteVideo" :muted="mutedStatus" class="w-1/2" playsinline autoplay></video>
  </div>

  <div class="w-full mt-7 flex flex-row justify-center gap-5">
    <div
      v-if="mutedStatus"
      class="h-10 w-10 cursor-pointer border rounded-3xl flex justify-center items-center"
      title="开启声音"
      @click="changeMuteStatus"
    >
      <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" class="w-7 h-7">
        <path
          d="M128 420.576v200.864h149.12l175.456 140.064V284.288l-169.792 136.288H128z m132.256-64l204.288-163.968a32 32 0 0 1 52.032 24.96v610.432a32 32 0 0 1-51.968 24.992l-209.92-167.552H96a32 32 0 0 1-32-32v-264.864a32 32 0 0 1 32-32h164.256zM752 458.656L870.4 300.8a32 32 0 1 1 51.2 38.4L792 512l129.6 172.8a32 32 0 0 1-51.2 38.4l-118.4-157.856-118.4 157.856a32 32 0 0 1-51.2-38.4l129.6-172.8-129.6-172.8a32 32 0 0 1 51.2-38.4l118.4 157.856z"
        ></path>
      </svg>
    </div>
    <div
      v-else
      class="h-10 w-10 cursor-pointer border rounded-3xl flex justify-center items-center"
      title="静音"
      @click="changeMuteStatus"
    >
      <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" class="w-7 h-7">
        <path
          d="M257.493333 322.4l215.573334-133.056c24.981333-15.413333 57.877333-7.914667 73.493333 16.746667 5.301333 8.373333 8.106667 18.048 8.106667 27.914666v555.989334C554.666667 819.093333 530.784 842.666667 501.333333 842.666667c-9.994667 0-19.786667-2.773333-28.266666-8L257.493333 701.6H160c-41.237333 0-74.666667-33.013333-74.666667-73.738667V396.138667c0-40.725333 33.429333-73.738667 74.666667-73.738667h97.493333z m26.133334 58.4a32.298667 32.298667 0 0 1-16.96 4.8H160c-5.888 0-10.666667 4.714667-10.666667 10.538667v231.733333c0 5.813333 4.778667 10.538667 10.666667 10.538667h106.666667c5.994667 0 11.872 1.664 16.96 4.8L490.666667 770.986667V253.013333L283.626667 380.8zM800.906667 829.653333a32.288 32.288 0 0 1-45.248-0.757333 31.317333 31.317333 0 0 1 0.768-44.693333c157.653333-150.464 157.653333-393.962667 0-544.426667a31.317333 31.317333 0 0 1-0.768-44.682667 32.288 32.288 0 0 1 45.248-0.757333c183.68 175.306667 183.68 460.010667 0 635.317333z m-106.901334-126.186666a32.288 32.288 0 0 1-45.248-1.216 31.328 31.328 0 0 1 1.237334-44.672c86.229333-80.608 86.229333-210.56 0-291.178667a31.328 31.328 0 0 1-1.237334-44.672 32.288 32.288 0 0 1 45.248-1.216c112.885333 105.546667 112.885333 277.418667 0 382.965333z"
        ></path>
      </svg>
    </div>

    <div
      class="h-10 w-10 cursor-pointer border rounded-3xl flex justify-center items-center bg-red-500 hover:bg-red-300"
      title="挂断"
      @click="handleHandout"
    >
      <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" class="w-7 h-7 fill-white">
        <path
          d="M446.272 573.44a509.653333 509.653333 0 0 1-81.92-100.650667c67.786667-30.474667 112.138667-93.461333 112.138667-169.792C476.48 194.592 388.96 106.666667 280.906667 106.666667 170.058667 106.666667 85.333333 188.746667 85.333333 302.997333c0 158.250667 82.56 328.554667 200.618667 439.658667 100.010667 94.122667 258.986667 161.738667 413.461333 174.570667 0.832 0.074667 1.674667 0.106667 2.517334 0.106666h41.162666a30.517333 30.517333 0 0 0 0-61.045333h-39.872c-140.672-11.978667-286.026667-73.930667-375.456-158.090667-106.410667-100.16-181.408-254.837333-181.408-395.2 0-80.106667 56.981333-135.285333 134.549334-135.285333 74.282667 0 134.549333 60.533333 134.549333 135.285333 0 60.309333-40.896 107.989333-103.008 123.349334a30.517333 30.517333 0 0 0-19.786667 43.658666c27.573333 53.312 66.037333 104.426667 111.573334 147.690667 51.264 48.693333 109.941333 86.112 172.053333 108.16a30.506667 30.506667 0 0 0 40.362667-24.064c10.453333-67.093333 61.621333-114.026667 126.442666-114.026667 74.272 0 134.549333 60.544 134.549334 135.285334 0 25.578667-7.04 50.026667-20.149334 71.253333a30.528 30.528 0 0 0 51.925334 32.074667A196.096 196.096 0 0 0 938.666667 723.050667c0-108.394667-87.530667-196.330667-195.573334-196.330667-83.072 0-151.210667 52.384-177.621333 128.864-42.368-19.552-82.773333-47.541333-119.2-82.144z"
        ></path>
      </svg>
    </div>

    <div
      v-if="micStatus"
      class="h-10 w-10 cursor-pointer border rounded-3xl flex justify-center items-center bg-gray-300 hover:bg-gray-100"
      title="关闭麦克风"
      @click="changeMicStatus"
    >
      <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" class="w-7 h-7">
        <path
          d="M412.032 592l-44.992 46.016a197.376 197.376 0 0 1-35.008-59.008A189.248 189.248 0 0 1 320 512V256c1.344-54.656 20.032-100.032 56-136C412.032 84.032 457.28 65.344 512 64c54.72 1.344 100.032 20.032 136 56 36.032 35.968 54.656 81.28 56 136v44.032l-64 64V256c-0.64-36.032-13.12-66.176-37.504-90.496C578.176 141.184 548.032 128.704 512 128c-35.968 0.64-66.176 13.184-90.496 37.504-24.32 24.32-36.8 54.528-37.504 90.496v256c0.64 30.72 9.984 57.344 28.032 80z m51.968 39.04a126.656 126.656 0 0 0 119.488-12.992A127.68 127.68 0 0 0 640.128 512v-56.96l64-64V512a189.632 189.632 0 0 1-96 166.016 187.776 187.776 0 0 1-95.488 26.048c-33.664 0-65.856-8.32-96.512-25.024l48-48z m-148.992 148.992l46.08-46.08A222.144 222.144 0 0 0 480 768h64c63.36-1.344 116.16-23.168 158.464-65.536 42.368-42.24 64.192-95.104 65.536-158.464V512c0-9.344 2.944-17.024 8.96-23.04a31.168 31.168 0 0 1 23.04-8.96c9.344 0 17.024 3.008 23.04 8.96 5.952 6.016 8.96 13.696 8.96 23.04v32c-2.048 81.344-30.208 149.12-84.48 203.52-54.4 54.272-122.24 82.432-203.52 84.48v64h64c9.344 0 17.024 3.008 23.04 8.96 5.952 6.016 8.96 13.696 8.96 23.04a31.168 31.168 0 0 1-8.96 23.04 31.168 31.168 0 0 1-23.04 8.96h-192a31.168 31.168 0 0 1-23.04-8.96 31.168 31.168 0 0 1-8.96-23.04c0-9.344 3.008-17.024 8.96-23.04a31.168 31.168 0 0 1 23.04-8.96h64v-64c-30.72 0-60.032-4.48-88-13.504a278.4 278.4 0 0 1-76.992-38.464z m-48-42.048A286.272 286.272 0 0 1 192 544V512c0-9.344 3.008-16.96 8.96-22.976a31.168 31.168 0 0 1 23.04-8.96c9.344 0 17.024 2.944 23.04 8.96 5.952 6.016 8.96 13.632 8.96 23.04v32c0 28.608 4.992 55.488 15.04 80.512 9.984 24.96 23.68 47.488 40.96 67.456l-44.992 45.952zM151.04 859.008a32.832 32.832 0 0 1-22.528 8.96 30.272 30.272 0 0 1-22.016-9.472 32 32 0 0 1-9.984-22.016 30.848 30.848 0 0 1 8.512-22.464l704-709.056a32.832 32.832 0 0 1 22.464-8.96c8.32 0 15.68 3.2 22.016 9.536a32.512 32.512 0 0 1 1.536 45.44l-704 708.032z"
        ></path>
      </svg>
    </div>

    <div
      v-if="!micStatus"
      class="h-10 w-10 cursor-pointer border rounded-3xl flex justify-center items-center bg-gray-300 hover:bg-gray-100"
      title="开启麦克风"
      @click="changeMicStatus"
    >
      <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" class="w-7 h-7">
        <path
          d="M486.4 972.8v-128.9728A332.8 332.8 0 0 1 179.2 512a25.6 25.6 0 0 1 51.2 0 281.6 281.6 0 0 0 563.2 0 25.6 25.6 0 1 1 51.2 0 332.8 332.8 0 0 1-307.2 331.8272V972.8h153.6a25.6 25.6 0 1 1 0 51.2h-358.4a25.6 25.6 0 1 1 0-51.2h153.6zM512 51.2a153.6 153.6 0 0 0-153.6 153.6v307.2a153.6 153.6 0 0 0 307.2 0V204.8a153.6 153.6 0 0 0-153.6-153.6z m0-51.2a204.8 204.8 0 0 1 204.8 204.8v307.2a204.8 204.8 0 1 1-409.6 0V204.8a204.8 204.8 0 0 1 204.8-204.8z"
        ></path>
      </svg>
    </div>
  </div>
</template>

<style scoped></style>
